import blue
import device
import GameWorld
import geo2
import graphicWrappers
import trinity
import uthread
import weakref
import world

class WorldSpaceScene(world.WorldSpace):
    __guid__ = 'world.CoreWorldSpaceScene'

    def __init__(self, worldSpaceID = None, instanceID = None, uglyMode = False):
        world.WorldSpace.__init__(self, worldSpaceID, instanceID)
        self.scene = None
        self.refreshingScene = False
        self.delayRefreshScene = None
        self.uglyMode = uglyMode
        self.properties = {}
        self.needsRefresh = False
        self.currentKynapseLineSet = None
        self.gps = sm.GetService('gps')
        self.prioritizedLoadManager = sm.GetService('prioritizedLoadManager')



    def ReloadProperties(self, previousSkyboxPath = None, overrideScene = None):
        if overrideScene:
            scene = overrideScene
        else:
            scene = self.scene
        simpleProperties = ('sunDiffuseColor', 'sunSpecularColor', 'ambientColor', 'fogColor', 'maxFogDistance', 'minFogDistance', 'maxFogAmount', 'environmentLightingMultiplier')
        for property in simpleProperties:
            if (((property in self.properties) and (self.properties[property] is not None)) and hasattr(scene, property)):
                setattr(scene, property, self.properties[property])

        if trinity.IsRightHanded():
            sunDirection = (self.properties.get('sunDirectionX', 1),
             self.properties.get('sunDirectionY', -1),
             self.properties.get('sunDirectionZ', -1))
        else:
            sunDirection = (self.properties.get('sunDirectionX', 1),
             self.properties.get('sunDirectionY', -1),
             self.properties.get('sunDirectionZ', 1))
        if not self.IsFM():
            scene.sunDirection = sunDirection
        if self.IsInterior():
            scene.ssao.enable = (self.properties.get('enableSSAO', False) and (not self.IsInUglyMode()))
        newSkyboxPath = None
        if ((('skyboxPath' in self.properties) and (self.properties['skyboxPath'] is not None)) and ((self.properties['skyboxPath'] != previousSkyboxPath) and (not self.IsFM()))):
            scene.SetSkyboxTexture(self.properties['skyboxPath'])
        return newSkyboxPath



    def IsInUglyMode(self):
        return self.uglyMode



    def SetUglyMode(self, uglyMode):
        self.uglyMode = uglyMode
        VISUALIZE_METHOD_DIFFUSE_ONLY = 20
        VISUALIZE_METHOD_NONE = 0
        if self.IsInterior():
            self.scene.shadowUpdatesPerFrame = (0 if self.IsInUglyMode() else 4)
            self.scene.ssao.enable = (self.properties.get('enableSSAO', False) and (not self.IsInUglyMode()))
            if self.IsInUglyMode():
                self.scene.visualizeMethod = VISUALIZE_METHOD_DIFFUSE_ONLY
            else:
                self.scene.visualizeMethod = VISUALIZE_METHOD_NONE
        elif self.IsInUglyMode():
            self.scene.visualizeMethod = VISUALIZE_METHOD_DIFFUSE_ONLY
        else:
            self.scene.visualizeMethod = VISUALIZE_METHOD_NONE



    def GetProperty(self, propertyName):
        if (propertyName in self.properties):
            return self.properties[propertyName]



    def IsSceneReady(self):
        return (self.scene is not None)



    def GetScene(self):
        if (self.scene is None):
            self._InitializeScene()
        return self.scene



    def IsFM(self):
        return ('_FM' in self.GetName())



    def _CreateScene(self):
        if self.IsFM():
            self.scene = trinity.WodExteriorScene()
        elif self.IsInterior():
            self.scene = trinity.Tr2InteriorScene()
        else:
            self.scene = trinity.WodScene()
            if (prefs.ini.HasKey('no3d') and (prefs.no3d.lower() == 'true')):
                self.scene.display = False
        graphicWrappers.Wrap(self.scene, convertSceneType=False)
        self.SetUglyMode(self.IsInUglyMode())
        self.physX = GameWorld.GWPhysXWrapper()
        trinity.InitializeApex(self.physX)
        if self.IsInterior():
            self.scene.ragdollScene = GameWorld.RagdollPhysicsScene()
        elif not self.IsFM():
            self.scene.city.ragdollScene = GameWorld.RagdollPhysicsScene()
        if hasattr(self.scene, 'SetID'):
            self.scene.SetID(self.GetWorldSpaceTypeID())



    def _LoadTerrainToScene(self):
        if ((self.scene is not None) and (not self.IsInterior())):
            if (prefs.ini.HasKey('noModels') and (prefs.noModels.lower() == 'true')):
                terrainPath = None
            else:
                terrainPath = self.GetTerrainPath()
            if terrainPath:
                device.SetEnvMipLevelSkipCount(inInterior=False)
                terrain = trinity.Load(terrainPath)
                if terrain:
                    self.scene.city.terrain = terrain



    def _LoadObjectToScene(self, worldSpaceObject):
        if (self.scene is not None):
            model = self.LoadPlaceableToScene(worldSpaceObject.GetRenderObject())
            return model



    def LoadPlaceableToScene(self, renderObject):
        if (self.scene is not None):
            if ('/skiprun' in blue.pyos.GetArg()):
                renderObject.AddToScene(self.scene)
            if getattr(renderObject, 'lightSources', False):
                self.needsRefresh = True
                self.DelayRefresh()
        return renderObject



    def _RemoveObjectFromScene(self, worldSpaceObject):
        self.RemovePlaceableFromScene(worldSpaceObject.GetRenderObject())



    def RemovePlaceableFromScene(self, renderObject):
        if (self.scene is not None):
            renderObject.RemoveFromScene(self.scene)
            if getattr(renderObject, 'lightSources', False):
                self.needsRefresh = True
                self.DelayRefresh()



    def _LoadObjectsToScene(self):
        if (self.scene is not None):
            for worldSpaceObject in self.GetObjects():
                self._LoadObjectToScene(worldSpaceObject)




    def _LoadOccludersToScene(self):
        if ((self.scene is not None) and self.IsInterior()):
            for occluder in self.GetOccluders():
                self._LoadObjectToScene(occluder)




    def _LoadGameSpecificToScene(self):
        return 



    def _InitializeScene(self):
        self._CreateScene()
        self._LoadTerrainToScene()
        self._LoadGameSpecificToScene()
        self.ReloadProperties(None)
        self.RefreshScene()



    def LoadEntities(self):
        self.entityClient = sm.GetService('entityClient')
        self.entitySpawnClient = sm.GetService('entitySpawnClient')
        scene = self.entityClient.GetEntityScene(self.GetWorldSpaceID())
        self.LoadObjectEntities(scene)
        self.LoadLightEntities(scene)
        self.LoadPhysicalPortalEntities(scene)
        self.LoadOccluderEntities(scene)



    def LoadLightEntities(self, scene):
        for light in self.GetLights():
            self._CreateLightEntity(scene, light)




    def LoadPhysicalPortalEntities(self, scene):
        for portal in self.GetPhysicalPortals():
            self._CreatePhysicalPortalEntity(scene, portal)




    def LoadOccluderEntities(self, scene):
        for occluder in self.GetOccluders():
            self._CreateOccluderEntity(scene, occluder)




    def _CreateLightEntity(self, scene, light):
        recipe = {'position': {'position': light.GetPosition(),
                      'rotation': light.GetRotation()},
         'light': light}
        e = self.entityClient.CreateEntityFromRecipe(scene, recipe, self.entitySpawnClient.GetNextSpawnID(self.GetWorldSpaceID(), 0))
        scene.CreateAndRegisterEntity(e)



    def _CreateObjectEntity(self, scene, obj):
        enlightenAreaRows = cfg.worldspaceEnlightenAreas.get(self.GetWorldSpaceTypeID(), {}).get(obj.GetID(), ())
        recipe = {'position': {'position': obj.GetPosition(),
                      'rotation': obj.GetRotation()},
         'graphic': {'graphicID': obj.GetGraphicID(),
                     'cellID': str(obj.GetCellID()),
                     'systemID': str(obj.GetSystemID()),
                     'objectID': obj.GetID(),
                     'enlightenOverrides': enlightenAreaRows},
         'collisionMesh': {'graphicID': obj.GetGraphicID()}}
        e = self.entityClient.CreateEntityFromRecipe(scene, recipe, self.entitySpawnClient.GetNextSpawnID(self.GetWorldSpaceID(), 0))
        scene.CreateAndRegisterEntity(e)



    def _CreatePhysicalPortalEntity(self, scene, physicalPortal):
        recipe = {'position': {'position': physicalPortal.GetPosition(),
                      'rotation': physicalPortal.GetRotation()},
         'physicalPortal': physicalPortal}
        e = self.entityClient.CreateEntityFromRecipe(scene, recipe, self.entitySpawnClient.GetNextSpawnID(self.GetWorldSpaceID(), 0))
        scene.CreateAndRegisterEntity(e)



    def _CreateOccluderEntity(self, scene, occluder):
        recipe = {'position': {'position': occluder.GetPosition(),
                      'rotation': occluder.GetRotation()},
         'occluder': occluder}
        e = self.entityClient.CreateEntityFromRecipe(scene, recipe, self.entitySpawnClient.GetNextSpawnID(self.GetWorldSpaceID(), 0))
        scene.CreateAndRegisterEntity(e)



    def ReloadScene(self):
        activeScene = self.IsActiveScene()
        self.scene = None
        self._InitializeScene()
        if activeScene:
            self._SetActiveScene(self.scene)



    def OnInvalidate(self, level):
        self.prePassTarget = None
        self.prePassZ = None
        self.ssaoDepth = None
        self.ssaoNormals = None
        self.lightAccumulationTarget = None
        if (self.rj is not None):
            trinity.device.scheduledRecurring.remove(self.rj)
        self.rj = None



    def OnCreate(self, device):
        self._SetActiveScene(self.scene)



    def IsActiveScene(self):
        if ((self.scene is not None) and (self.scene == self._GetActiveScene())):
            return True
        else:
            return False



    def RefreshScene(self):
        if ((self.scene is not None) and (not self.refreshingScene)):
            self.refreshingScene = True
            uthread.new(self._RefreshScene).context = 'worldSpaceScene::RefreshScene'



    def _RefreshScene(self):
        blue.synchro.Yield()
        self.refreshingScene = False
        self.needsRefresh = False
        self.scene.Refresh()
        if self.delayRefreshScene:
            self.delayRefreshScene.kill()
            self.delayRefreshScene = None



    def DelayRefresh(self, secs = 15):
        if not self.delayRefreshScene:
            self.delayRefreshScene = uthread.new(self._DelayRefresh, secs)
            self.delayRefreshScene.context = 'worldSpaceScene::DelayRefresh'



    def _DelayRefresh(self, secs):
        blue.synchro.Sleep((secs * 1000))
        self.delayRefreshScene = None
        self.RefreshScene()



    def ProcessWorldTypeAdd(self, type, obj):
        return self._LoadObjectToScene(obj)



    def ProcessWorldTypeDelete(self, type, objectID):
        obj = self._GetData(type, objectID)
        if obj:
            self._RemoveObjectFromScene(obj)



    def _CreateObject(self, row):
        obj = world.WorldSpace._CreateObject(self, row)
        if obj.IsEntity():
            return 
        else:
            return obj



    def _CreatePhysicalPortal(self, row):
        portal = world.WorldSpace._CreatePhysicalPortal(self, row)
        return portal



    def LoadObjectModelFromID(self, objectID):
        obj = self.GetObject(objectID)
        if obj:
            return self.LoadObjectModel(obj)



    def LoadObjectModel(self, obj):
        model = self.ProcessWorldTypeAdd(const.world.TYPE_OBJECT, obj)
        return model



    def _ConnectPhysicalPortal(self, portal):
        try:
            scene = sm.GetService('graphicClient').GetScene(self.GetWorldSpaceID())
        except AttributeError:
            scene = self.scene
        if scene:
            cells = scene.cells
            cellAObj = None
            cellBObj = None
            for cell in cells:
                if (portal.GetCellA() == cell.name):
                    cellAObj = cell
                else:
                    if (portal.GetCellB() == cell.name):
                        cellBObj = cell

            portal.GetRenderObject().ConnectCells(cellAObj, cellBObj)



    def AddObjectLightOverride(self, objectID, lightID, row):
        if self.HasObject(objectID):
            currentObject = self.GetObject(objectID)
            if not currentObject.lightOverrides.has_key(lightID):
                lightOverride = world.ObjectLightOverride(self.GetWorldSpaceTypeID(), objectID, lightID, row)
                currentObject.lightOverrides[lightID] = lightOverride
                currentObject.AttachLightOverride(lightID)
                currentObject.Refresh()



    def RemoveObject(self, objectID):
        obj = self.GetObject(objectID)
        if obj:
            gwo = obj.GetGameWorldObject()
            if gwo:
                gw = self.GetGameWorld()
                gw.RemoveStaticShape(gwo)
            if obj.IsRenderObjectLoaded():
                self.RemovePlaceableFromScene(obj.GetRenderObject())
            del self.loadedData[const.world.TYPE_OBJECT][objectID]



    def LoadProperties(self):
        return 




